package org.yan.kangaroo.auth.config.security.filter;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.yan.kangaroo.auth.config.properties.IgnoredUrlProperties;
import org.yan.kangaroo.auth.config.security.SecurityConstant;
import org.yan.kangaroo.auth.config.security.SecurityUserDetails;
import org.yan.kangaroo.auth.constant.response.ResponseCodeEnum;
import org.yan.kangaroo.auth.util.JwtUtils;
import org.yan.kangaroo.common.response.ResponseUtils;
import org.yan.kangaroo.common.response.Result;
import org.yan.kangaroo.common.util.JsonUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author wangx
 * @date 11/11/2019 15:40
 */
@Slf4j
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
    private IgnoredUrlProperties ignoredUrlProperties;

    private StringRedisTemplate redisTemplate;


    public JwtAuthenticationFilter(AuthenticationManager authenticationManager, AuthenticationEntryPoint authenticationEntryPoint) {
        super(authenticationManager, authenticationEntryPoint);
    }

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager, IgnoredUrlProperties ignoredUrlProperties, StringRedisTemplate redisTemplate) {
        super(authenticationManager);
        this.ignoredUrlProperties = ignoredUrlProperties;
        this.redisTemplate = redisTemplate;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String requestUrl = request.getRequestURI();
        PathMatcher pathMatcher = new AntPathMatcher();
        for (String url : ignoredUrlProperties.getUrls()
        ) {
            if (pathMatcher.match(url, requestUrl)) {
                chain.doFilter(request, response);
                return;
            }
        }

        String token = request.getHeader(SecurityConstant.TOKEN_HEADER);
        boolean notValidate = StringUtils.isBlank(token) || (!token.startsWith(SecurityConstant.TOKEN_PREFIX));
        if (notValidate) {
            chain.doFilter(request, response);
            return;
        }

        UsernamePasswordAuthenticationToken authenticationToken = getAuthentication(token, response);
        if (authenticationToken == null) {
            return;
        }
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(String token, HttpServletResponse response) {
        Claims claims;
        try {
            claims = JwtUtils.getTokenContent(token);
            String json = claims.get(SecurityConstant.TOKEN_USER_INFO).toString();
            SecurityUserDetails user = JsonUtils.getInstance().fromJson(json, SecurityUserDetails.class);
            return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
        } catch (ExpiredJwtException e) {
            log.error("[getTokenAuthorities] error:", e);
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            ResponseUtils.outAsJson(response, Result.error(ResponseCodeEnum.LOGIN_FAILED));
            return null;
        } catch (Exception e) {
            log.error("[getTokenAuthorities] error:", e);
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            ResponseUtils.outAsJson(response, Result.error(ResponseCodeEnum.AUTHENTICATION_FAILED));
            return null;
        }
    }
}

